#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include"Logger.hpp"
#include<string>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<cstdlib>
#include<strings.h>
#include<functional>//代码解耦

using callback_t =std::function<std::string(const std::string&word,const std::string &whoip,uint16_t whoport)>;

static const int gdefaultsockfa=-1;
class DictServer
{
public:
    DictServer(uint16_t port,callback_t cb):
    _cb(cb),
    _port(port),
    _sockfd(gdefaultsockfa),
    _isrunning(false)
    {}
    void Init()
    {

        _sockfd=socket(AF_INET,SOCK_DGRAM,0);
        //协议族（决定ip地址和端口号格式，套接字类型，协议类型，设为0让系统自动选择
        //失败返回-1；
        if(_sockfd<0)
        {
            LOG(Loglevel::FATAL)<<"creat socket error";
            exit(1);
        }
        LOG(Loglevel::INFO)<<"creat socket success :"<<_sockfd;//3

        //bind 绑定,给套接字命名，附上IP和端口
        //0成功
        //2.1:填充IP和端口
        //我们有把socket和file关联起来吗？No！！！！

        struct sockaddr_in local;
        //struct sockaddr_in 是一个 结构体，定义在头文件 <netinet/in.h>中，用于表示 IPv4 的套接字地址信息
        //包括：IP地址，端口号，地址族//套接字=ip+端口号

        bzero(&local,sizeof(local));
        //将指针 s指向的内存区域的头 n个字节，全部设置为 0（即清零）

        local.sin_family=AF_INET;
        local.sin_port=htons(_port);//主机转网络；主机存储方案转为网络序列 

        //什么叫任意IP绑定，如果绑定了固定的，只能收这个的信息。所以我们不明确具体的IP只要是发给我对应的主机的，对应的port我都能收到！！！
        local.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IPbind
        //local.sin_addr.s_addr=inet_addr(_ip.c_str());//1.将字符串转整数ip 2.整数IP是网络序列的

        //2.2和socket进行bind
        int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
        if(n<0)
        {
            LOG(Loglevel::FATAL)<<"bind socket error";
             perror("bind");
            exit(2);
        }
        LOG(Loglevel::INFO)<<"bind socket success :"<<_sockfd;//3

    }
    void Start()
    {
        //所有的服务器都是死循环
        _isrunning=true;
        while(_isrunning)
        {
            char buffer[1024];
            buffer[0]=0;//清空数据
            struct sockaddr_in peer;
            socklen_t len=sizeof(peer);//输入输出型的，必须设置结构体大小，每次都要设置
            //1.读取数据
            ssize_t n=recvfrom(_sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,&len);
            if(n>0)
            {
                 //client是谁啊？？ip和端口给我！
                uint16_t clientport = ntohs(peer.sin_port);
                std::string clientip = inet_ntoa(peer.sin_addr);//把四字节网络IP地址转为字符串并以主机序列形式呈现；//man inet_addr;
                std::string word=buffer;
                LOG(Loglevel::DEBUG) << "用户查找: " << word;
                //回调方法
                std::string result=_cb(word,clientip,clientport);

                // buffer[n] = 0;
                // LOG(Loglevel::DEBUG) << "[" << clientip 
                //     << ":" << clientport << "]# " << buffer;
               
                // LOG(Loglevel::DEBUG)<<"client say# "<<buffer;
             

                // std::string echo_string =  "server echo# ";
                // echo_string +=buffer;

                //sendto:sendto()函数用于通过一个套接字（通常是 UDP 套接字）向指定的目标地址（IP + 端口）发送数据，是 UDP 网络通信中 “发送数据” 的标准方法。
                //1.socket既可以读，又可以写，UDP支持全双工通信；
                sendto(_sockfd,result.c_str(),result.size(),0,(struct sockaddr*)&peer,len);

            }
        }
        _isrunning=false;

    }
    void Stop()
    {
        _isrunning=false;
    }
    ~DictServer(){}
private:
    int _sockfd;
    uint16_t _port;//要给对方，则要先给网络
    //不建议绑定固定的ip
    //std::string _ip;//字符串风格的点分十进制-->字符串-可读性好，but要占11个字节；每个值取值范围为[0,255]--方便网络发送;
    //所以要转化，转为4字节；使用接口inet_addr;

    callback_t _cb;//服务器将来要做的；

    bool _isrunning;//服务器是否在运行；
};